/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 actor_sequence_cache.cpp

	$Header: /heroes4/actor_sequence_cache.cpp $

	$NoKeywords: $

 ************************************************************************/

#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "actor_sequence_cache.h"

#include <functional>
#include <map>
#include <utility>

#include "actor_sequence.h"
#include "conversion_cache.h"
#include "shared_ptr.h"
#include "string_insensitive_compare.h"

// ------------------------------------------------------------------------------
// Unnamed namespace
// ------------------------------------------------------------------------------

namespace
{

	// ------------------------------------------------------------------------------
	// t_sequence_cache_data class
	// ------------------------------------------------------------------------------

	class t_sequence_cache_data
		:	public t_conversion_cache_data< t_actor_sequence_24, t_actor_sequence >
	{
	public:
		// Constructor
		t_sequence_cache_data(
			std::string const &		name,
			t_screen_point const &	offset );

	private:
		// Data members
		t_screen_point	m_offset;

		// Member functions
		virtual t_actor_sequence *	do_read(
										std::streambuf &		stream,
										t_progress_handler *	handler_ptr );
	};

	t_sequence_cache_data::t_sequence_cache_data(
		std::string const &		name,
		t_screen_point const &	offset )
		:	t_conversion_cache_data< t_actor_sequence_24, t_actor_sequence >( name ),
			m_offset( offset )
	{
	}

	t_actor_sequence * t_sequence_cache_data::do_read(
		std::streambuf &		stream,
		t_progress_handler *	handler_ptr )
	{
		t_actor_sequence * result = 0;
		t_actor_sequence_24 source;
		if ( ::read( stream, source ) )
			result = new t_actor_sequence( source, m_offset );
		return result;
	}

	// ------------------------------------------------------------------------------
	// t_sequence_cache class
	// ------------------------------------------------------------------------------

	class t_sequence_cache
		:	public t_conversion_cache< t_actor_sequence_24, t_actor_sequence >
	{
	public:
		// Constructor
		t_sequence_cache(
			std::string const &		name,
			t_screen_point const &	offset );
	};

	t_sequence_cache::t_sequence_cache(
		std::string const &		name,
		t_screen_point const &	offset )
		:	t_conversion_cache< t_actor_sequence_24, t_actor_sequence >(
				new t_sequence_cache_data( name, offset ) )
	{
	}

	// ------------------------------------------------------------------------------
	// t_sequence_cache_key struct
	// ------------------------------------------------------------------------------

	struct t_sequence_cache_key
	{
	public:
		// Data members
		std::string		name;
		t_screen_point	offset;

		// Constructor
		t_sequence_cache_key() {}
		t_sequence_cache_key(
			std::string const &		name_arg,
			t_screen_point const &	offset_arg );
	};

	inline t_sequence_cache_key::t_sequence_cache_key(
		std::string const &		name_arg,
		t_screen_point const &	offset_arg )
		:	name( name_arg ),
			offset( offset_arg )
	{
	}

	int compare_sequence_cache_key(
		t_sequence_cache_key const & first,
		t_sequence_cache_key const & second )
	{
		int result = string_insensitive_compare( first.name, second.name );
		if ( result != 0 )
			return result;

		result = first.offset.x - second.offset.x;
		if ( result != 0 )
			return result;

		return first.offset.y - second.offset.y;
	}

	// ------------------------------------------------------------------------------
	// t_sequence_cache_key_less struct
	// ------------------------------------------------------------------------------

	struct t_sequence_cache_key_less
		:	public	std::binary_function<
						t_sequence_cache_key,
						t_sequence_cache_key,
						bool >
	{
	public:
		// Operator
		bool	operator()(
					t_sequence_cache_key const & first,
					t_sequence_cache_key const & second ) const;
	};

	inline bool t_sequence_cache_key_less::operator()(
		t_sequence_cache_key const & first,
		t_sequence_cache_key const & second ) const
	{
		return compare_sequence_cache_key( first, second ) < 0;
	};

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	typedef t_shared_ptr< t_sequence_cache >	t_sequence_cache_ptr;

	typedef	std::map<
				t_sequence_cache_key,
				t_sequence_cache_ptr,
				t_sequence_cache_key_less >		t_sequence_cache_ptr_map;

	// ------------------------------------------------------------------------------
	// t_scaled_sequence_cache_data class
	// ------------------------------------------------------------------------------

	class t_scaled_sequence_cache_data
		:	public t_conversion_cache_data< t_actor_sequence_24, t_actor_sequence >
	{
	public:
		// Constructor
		t_scaled_sequence_cache_data(
			std::string const &		name,
			t_screen_point const &	offset,
			double					ratio );

	private:
		// Data members
		t_screen_point	m_offset;
		double			m_ratio;

		// Member functions
		virtual t_actor_sequence *	do_read(
										std::streambuf &		stream,
										t_progress_handler *	handler_ptr );
	};

	t_scaled_sequence_cache_data::t_scaled_sequence_cache_data(
		std::string const &		name,
		t_screen_point const &	offset,
		double					ratio )
		:	t_conversion_cache_data< t_actor_sequence_24, t_actor_sequence >( name ),
			m_offset( offset ),
			m_ratio( ratio )
	{
	}

	t_actor_sequence * t_scaled_sequence_cache_data::do_read(
		std::streambuf &		stream,
		t_progress_handler *	handler_ptr )
	{
		t_actor_sequence * result = 0;
		t_actor_sequence_24 source;
		if ( ::read( stream, source ) )
			result = new t_actor_sequence( source, m_ratio, m_offset );
		return result;
	}

	// ------------------------------------------------------------------------------
	// t_scaled_sequence_cache class
	// ------------------------------------------------------------------------------

	class t_scaled_sequence_cache
		:	public t_conversion_cache< t_actor_sequence_24, t_actor_sequence >
	{
	public:
		// Constructor
		t_scaled_sequence_cache(
			std::string const &		name,
			t_screen_point const &	offset,
			double					ratio );
	};

	t_scaled_sequence_cache::t_scaled_sequence_cache(
		std::string const &		name,
		t_screen_point const &	offset,
		double					ratio )
		:	t_conversion_cache< t_actor_sequence_24, t_actor_sequence >(
				new t_scaled_sequence_cache_data( name, offset, ratio ) )
	{
	}

	// ------------------------------------------------------------------------------
	// t_scaled_sequence_cache_key struct
	// ------------------------------------------------------------------------------
	struct t_scaled_sequence_cache_key : public t_sequence_cache_key
	{
	public:
		// Data members
		double	ratio;

		// Constructor
		t_scaled_sequence_cache_key() {}
		t_scaled_sequence_cache_key(
			std::string const &		name_arg,
			t_screen_point const &	offset_arg,
			double					ratio_arg );
	};

	inline t_scaled_sequence_cache_key::t_scaled_sequence_cache_key(
		std::string const &		name_arg,
		t_screen_point const &	offset_arg,
		double					ratio_arg )
		:	t_sequence_cache_key( name_arg, offset_arg ),
			ratio( ratio_arg )
	{
	}

	// ------------------------------------------------------------------------------
	// t_scaled_sequence_cache_key_less struct
	// ------------------------------------------------------------------------------
	
	struct t_scaled_sequence_cache_key_less
		:	public	std::binary_function<
						t_scaled_sequence_cache_key,
						t_scaled_sequence_cache_key,
						bool >
	{
	public:
		// Operator
		bool	operator()(
					t_scaled_sequence_cache_key const & first,
					t_scaled_sequence_cache_key const & second ) const;
	};

	inline bool t_scaled_sequence_cache_key_less::operator()(
		t_scaled_sequence_cache_key const & first,
		t_scaled_sequence_cache_key const & second ) const
	{
		int base_compare = compare_sequence_cache_key( first, second );
		return base_compare < 0 || ( base_compare == 0 && first.ratio < second.ratio );
	}

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	class t_scaled_sequence_cache_ptr
		:	public t_shared_ptr< t_scaled_sequence_cache >
	{
	public:
		// Constructor
		t_scaled_sequence_cache_ptr( t_scaled_sequence_cache * ptr );
	};

	inline t_scaled_sequence_cache_ptr::t_scaled_sequence_cache_ptr( t_scaled_sequence_cache * ptr )
		:	t_shared_ptr< t_scaled_sequence_cache >( ptr )
	{
	}

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	typedef	std::map<
				t_scaled_sequence_cache_key,
				t_scaled_sequence_cache_ptr,
				t_scaled_sequence_cache_key_less >	t_scaled_sequence_cache_ptr_map;

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	inline t_sequence_cache_ptr_map & get_sequence_cache_ptr_map()
	{
		static t_sequence_cache_ptr_map sequence_cache_ptr_map;
		return sequence_cache_ptr_map;
	}

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	inline t_scaled_sequence_cache_ptr_map & get_scaled_sequence_cache_ptr_map()
	{
		static t_scaled_sequence_cache_ptr_map scaled_sequence_cache_ptr_map;
		return scaled_sequence_cache_ptr_map;
	}

} // Unnamed namespace

// ------------------------------------------------------------------------------
// Details namespace
// ------------------------------------------------------------------------------

namespace actor_sequence_cache_details
{

	t_initializer::t_initializer()
	{
		get_sequence_cache_ptr_map();
		get_scaled_sequence_cache_ptr_map();
	}

} // Details namespace

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
t_abstract_cache< t_actor_sequence > & get_actor_sequence_cache(
	std::string const &		name,
	t_screen_point const &	offset )
{
	t_sequence_cache_ptr_map & sequence_cache_ptr_map = get_sequence_cache_ptr_map();

	t_sequence_cache_key key( name, offset );	
	t_sequence_cache_ptr_map::iterator map_iter = sequence_cache_ptr_map.find( key );
	if ( map_iter == sequence_cache_ptr_map.end() )
	{
		t_sequence_cache_ptr cache_ptr( new t_sequence_cache( name, offset ) );
		map_iter = sequence_cache_ptr_map.insert( std::make_pair( key, cache_ptr ) ).first;
	}

	return *map_iter->second;
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
t_abstract_cache< t_actor_sequence > & get_scaled_actor_sequence_cache(
	std::string const &		name,
	t_screen_point const &	offset,
	double					ratio )
{
	t_scaled_sequence_cache_ptr_map & scaled_sequence_cache_ptr_map = get_scaled_sequence_cache_ptr_map();

	t_scaled_sequence_cache_key key( name, offset, ratio );
	t_scaled_sequence_cache_ptr_map::iterator map_iter = scaled_sequence_cache_ptr_map.find( key );
	if ( map_iter == scaled_sequence_cache_ptr_map.end() )
	{
		t_scaled_sequence_cache_ptr cache_ptr( new t_scaled_sequence_cache( name, offset, ratio ) );
		map_iter = scaled_sequence_cache_ptr_map.insert( std::make_pair( key, cache_ptr ) ).first;
	}

	return *map_iter->second;
}
